﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace server.app
{
    //#region StarManager
    public sealed class StarManager
    {
        const int THREAD_SLEEP = 1000;
        const int SAVE_INERVAL = 1000 * 10;

        public int AllStarCount { get { return _starDic.Count; } }
        public int FPS { get { return _timeStat.FPS; } }

        Dictionary<string, Star> _starDic = new Dictionary<string, Star>();
        World _world = null;
        comm.TimeStat _timeStat = new comm.TimeStat();
        comm.Timing _timing = new comm.Timing();

        public StarManager(World world)
        {
            _world = world;

            #region loop update
            comm.ThreadHelper.Run(() =>
            {
                while (true)
                {
                    _timeStat.Update();
                    var beginSave = _timing.GetElapsedTime(_timeStat, SAVE_INERVAL) > 0;
                    foreach (var star in _starDic.Values)
                    {
                        star.Update(_timeStat);
                        if (beginSave)
                        {
                            star.Save();
                            //Console.WriteLine("StarManager save!");
                        }
                    }
                    Thread.Sleep(THREAD_SLEEP);
                }
            });
            #endregion
        }

        public bool InitAllTable(SQLx sqlx)
        {
            var allStar = _world.DefineFile.GetAllStar();
            foreach (var starId in allStar)
            {
                var dStar = _world.DefineFile.GetStar(starId);
                var dStarData = _world.DefineFile.GetStarData(starId);

                if (!_world.DataManager.TableStar.CreateStar(sqlx, new comm.objst.TableStar { StarId = starId, Name = starId }))
                {
                    return false;
                }

                var facBaseSpaceDic = new Dictionary<string, string>();//<SpaceId,FacId>

                #region init facs
                foreach (var facId in dStarData.FactionDic.Keys)
                {
                    var fac = dStarData.FactionDic[facId];
                    var realFacId = dStar.ToRealFactionId(facId);
                    foreach (var spaceId in fac.BaseSpaceIdList)
                    {
                        facBaseSpaceDic.Add(dStar.ToRealSpaceId(spaceId), realFacId);
                    }
                    if (!_world.DataManager.TableFaction.CreateFaction(sqlx, new comm.objst.TableFaction
                    {
                        StarId = starId,
                        FacId = realFacId,
                        Name = fac.Name,
                        FlagImageBytes = fac.FlagImageBytes
                    }))
                    {
                        return false;
                    }
                }
                #endregion

                #region init stars
                foreach (var areaId in dStarData.AreaDic.Keys)
                {
                    var A = 'A';
                    var area = dStarData.AreaDic[areaId];
                    for (int x = 0; x < area.Width; x++)
                    {
                        for (int y = 0; y < area.Height; y++)
                        {
                            var space = area.Spaces[x, y];
                            var realSpaceId = dStar.ToRealSpaceId(space.SpaceId);
                            var tableSpace = new comm.objst.TableSpace
                            {
                                StarId = starId,
                                SpaceId = realSpaceId,
                                Name = A + y.ToString(),
                                OccupyType = comm.objst.SpaceOccupyType.EMPTY,
                                State = comm.objst.SpaceState.WAR_END
                            };
                            if (facBaseSpaceDic.ContainsKey(realSpaceId))
                            {
                                tableSpace.FactionId = facBaseSpaceDic[realSpaceId];
                                tableSpace.OccupyType = comm.objst.SpaceOccupyType.FAC;
                            }
                            if (!_world.DataManager.TableStar.CreateSpace(sqlx, tableSpace))
                            {
                                return false;
                            }
                        }
                        A++;
                    }
                }
                #endregion
            }
            return true;
        }
        public void LoadAllStar()
        {
            var allStar = _world.DefineFile.GetAllStar();
            foreach (var starId in allStar)
            {
                _starDic.Add(starId, _world.DataManager.CreateStar(starId));
            }
        }

        public Star GetStar(string starId)
        {
            Star obj = null;
            _starDic.TryGetValue(starId, out obj);
            return obj;
        }
        public Star[] GetAllStar()
        {
            var list = new List<Star>();
            foreach (var item in _starDic.Values)
            {
                list.Add(item);
            }
            return list.ToArray();
        }
        public comm.objst.StarInfo[] GetAllStarInfo()
        {
            var list = new List<comm.objst.StarInfo>();
            foreach (var item in _starDic.Values)
            {
                list.Add(item.GetStarInfo());
            }
            return list.ToArray();
        }
        public Dictionary<string, comm.objst.FactionInfo[]> GetAllFactionInfo()
        {
            var dic = new Dictionary<string, comm.objst.FactionInfo[]>();
            foreach (var item in _starDic.Values)
            {
                dic.Add(item.StarId, item.GetAllFactionInfo());
            }
            return dic;
        }
    }
    //#endregion

    //#region Star
    public abstract class Star : comm.world.BObject
    {
        #region Faction
        public class Faction
        {
            string _facationId = null;
            comm.objst.DStar.Data.Faction _dFaction = null;
            Star _star = null;
            World _world = null;
            comm.objst.TableFaction _tableFaction = null;

            public Faction(Star star, string factionId, comm.objst.DStar.Data.Faction dFaction)
            {
                _star = star;
                _facationId = factionId;
                _dFaction = dFaction;
                _world = _star.World;
                _tableFaction = _world.DataManager.TableFaction.GetFaction(_facationId);
            }

            public void Save()
            {
                _world.DataManager.TableFaction.UpdateFaction(_tableFaction);
            }
            public bool FindBaseSpace(string spaceId)
            {
                foreach (var id in _dFaction.BaseSpaceIdList)
                {
                    var realSpaceId = _star.ToRealSpaceId(id);
                    if (spaceId == realSpaceId)
                    {
                        return true;
                    }
                }
                return false;
            }
            public comm.objst.SpaceLoc GetNewHeroSpaceLoc()
            {
                foreach (var spaceId in _dFaction.BaseSpaceIdList)
                {
                    var realSpaceId = _star.ToRealSpaceId(spaceId);
                    var space = _star.GetSpace(realSpaceId);
                    if (space != null && space.HasMap)
                    {
                        var ev = space.FindMapEventCommand(comm.lib.MapEvent.CommandType.NEW_HERO_LOC);
                        if (ev != null)
                        {
                            return new comm.objst.SpaceLoc
                            {
                                StarId = _star.StarId,
                                SpaceId = realSpaceId,
                                LocX = ev.LocX,
                                LocY = ev.LocY
                            };
                        }
                    }
                }
                return null;
            }
            public comm.objst.FactionInfo GetFactionInfo()
            {
                return new comm.objst.FactionInfo
                {
                    FactionId = _facationId,
                    Name = _tableFaction.Name,
                    HeroCount = _tableFaction.HeroCount,
                    Color = _dFaction.Color,
                    FlagImageBytes = _tableFaction.FlagImageBytes
                };
            }
        }
        #endregion

        //#region Space
        public class SSpace : comm.world.BObject
        {
            #region MapEvent
            public class MapEvent
            {
                public int LocX { get; set; }
                public int LocY { get; set; }
                public comm.lib.MapEvent Event { get; set; }
            }
            #endregion

            public const int BATTLE_BEGIN_COOLDOWN = 1000 * 15;//15s
            public const int BATTLE_FAR_REINFORCE_COOLDOWN = 1000 * 30;//远征 30s
            public const int BATTLE_END_COOLDOWN = 1000 * 60;//60s

            public bool HasMap { get; private set; }
            public Space Space { get; private set; }
            public string SpaceId { get; private set; }

            List<MapEvent> _mapEventList = new List<MapEvent>();
            List<string> _spaceLinkList = new List<string>();

            Dictionary<string, int> _heroCountDic = new Dictionary<string, int>();
            Dictionary<comm.lib.MapEvent.CommandType, int[]> _cmdLocDic = new Dictionary<comm.lib.MapEvent.CommandType, int[]>();
            comm.objst.DStar.Data.Space _dSpaceData = null;
            Area _area = null;
            World _world = null;
            comm.objst.TableSpace _tableSpace = null;

            public SSpace(Area area, string spaceId, comm.objst.DStar.Data.Space dSpaceData)
            {
                _area = area;
                _dSpaceData = dSpaceData;
                _world = _area.World;
                SpaceId = spaceId;
                _tableSpace = _world.DataManager.TableStar.GetSpace(SpaceId);

                var mapData = _world.DefineFile.GetMapData(_dSpaceData.MapId);
                if (mapData != null)
                {
                    HasMap = true;
                    mapData.AddExObjDic(dSpaceData.ObjDic);

                    #region _mapEventList
                    var sobjDic = mapData.FindObj<comm.objst.DMap.Data.SObj>();
                    foreach (var sobj in sobjDic.Values)
                    {
                        foreach (var obj in sobj.EVDic.Values)
                        {
                            if (obj.Value is comm.lib.MapEvent)
                            {
                                var ev = (comm.lib.MapEvent)obj.Value;
                                _mapEventList.Add(new MapEvent { LocX = sobj.LocX, LocY = sobj.LocY, Event = ev });

                                #region _spaceLinkList
                                if (ev is comm.lib.MapEvent.MoveToSpace)
                                {
                                    _spaceLinkList.Add(((comm.lib.MapEvent.MoveToSpace)ev).SpaceId);
                                }
                                else if (ev is comm.lib.MapEvent.MoveToLocLabel)
                                {
                                    _spaceLinkList.Add(((comm.lib.MapEvent.MoveToLocLabel)ev).SpaceId);
                                }
                                #endregion
                            }
                        }
                    }
                    #endregion

                    Space = _world.SpaceManager.CreateSpace(_dSpaceData.MapId, _area.Star.StarId, SpaceId, dSpaceData.ObjDic);
                    Space.OnObjEnter += new EventHandler<EnterEventArgs>(Space_OnObjEnter);
                    Space.OnObjLeave += new EventHandler<LeaveEventArgs>(Space_OnObjLeave);
                }
                if (!HasMap)
                {
                    _tableSpace.OccupyType = comm.objst.SpaceOccupyType.EMPTY;
                    _tableSpace.State = comm.objst.SpaceState.NOT_OPEN;
                }
            }

            void UpdateHeroCunt(lib.Hero[] heros, int count)
            {
                foreach (var hero in heros)
                {
                    if (!_heroCountDic.ContainsKey(hero.FactionId))
                    {
                        _heroCountDic.Add(hero.FactionId, 0);
                    }
                    _heroCountDic[hero.FactionId] += count;
                    _heroCountDic[hero.FactionId] = Math.Max(0, _heroCountDic[hero.FactionId]);
                }
            }

            void Space_OnObjEnter(object sender, EnterEventArgs e)
            {
                lock (_heroCountDic)
                {
                    UpdateHeroCunt(e.ObjList.ToArray().Find<lib.Hero>(), 1);
                }
            }
            void Space_OnObjLeave(object sender, LeaveEventArgs e)
            {
                lock (_heroCountDic)
                {
                    UpdateHeroCunt(e.ObjList.ToArray().Find<lib.Hero>(), -1);
                }
            }

            public void Save()
            {
                _world.DataManager.TableStar.UpdateSpace(_tableSpace);
            }
            public MapEvent FindMapEventCommand(comm.lib.MapEvent.CommandType cmdType)
            {
                var evs = FindMapEvent<comm.lib.MapEvent.Command>();
                foreach (var item in evs)
                {
                    if (item.Event is comm.lib.MapEvent.Command)
                    {
                        var cmd = (comm.lib.MapEvent.Command)item.Event;
                        if (cmd.Type == cmdType)
                        {
                            return item;
                        }
                    }
                }
                return null;
            }
            public MapEvent[] FindMapEvent<T>()
            {
                var list = new List<MapEvent>();
                foreach (var item in _mapEventList)
                {
                    list.Add(item);
                }
                return list.ToArray();
            }
            public comm.objst.SpaceInfo GetSpaceInfo()
            {
                var list = new List<comm.objst.SpaceHeroStat>();
                foreach (var key in _heroCountDic.Keys)
                {
                    list.Add(new comm.objst.SpaceHeroStat
                    {
                        FactionId = key,
                        HeroCount = _heroCountDic[key]
                    });
                }
                return new comm.objst.SpaceInfo
                {
                    SpaceId = SpaceId,
                    Name = _tableSpace.Name,
                    FactionId = _tableSpace.FactionId,
                    SpaceType = _dSpaceData.SpaceType,
                    OccupyType = _tableSpace.OccupyType,
                    State = _tableSpace.State,
                    DisplayFlag = _area.Star.FindBaseSpace(SpaceId),
                    HeroStatList = list
                };
            }

            protected override void OnUpdate(comm.TimeStat timeStat)
            {
            }
        }
        //#endregion

        #region Area
        public class Area : comm.world.BObject
        {
            public World World { get; private set; }
            public Star Star { get; private set; }

            comm.objst.DStar.Data.Area _areaData = null;

            SSpace[,] _spaces = null;
            Dictionary<string, SSpace> _spaceDic = new Dictionary<string, SSpace>();

            public Area(Star star, comm.objst.DStar.Data.Area areaData)
            {
                World = star.World;
                Star = star;
                _areaData = areaData;
                _spaces = new SSpace[_areaData.Width, _areaData.Height];
                for (int x = 0; x < _areaData.Width; x++)
                {
                    for (int y = 0; y < _areaData.Height; y++)
                    {
                        var spaceData = _areaData.Spaces[x, y];
                        var spaceId = Star.ToRealSpaceId(spaceData.SpaceId);
                        var space = new SSpace(this, spaceId, spaceData);
                        _spaces[x, y] = space;
                        _spaceDic.Add(spaceId, space);
                    }
                }
            }

            public void Save()
            {
                for (int x = 0; x < _areaData.Width; x++)
                {
                    for (int y = 0; y < _areaData.Height; y++)
                    {
                        _spaces[x, y].Save();
                    }
                }
            }
            public SSpace GetSpace(string spaceId)
            {
                SSpace obj = null;
                _spaceDic.TryGetValue(spaceId, out obj);
                return obj;
            }
            public SSpace GetSpace(int x, int y)
            {
                return _spaces[x, y];
            }
            public comm.objst.SpaceInfo[] GetAllSpaceInfo()
            {
                var list = new List<comm.objst.SpaceInfo>();
                foreach (var space in _spaceDic.Values)
                {
                    list.Add(space.GetSpaceInfo());
                }
                return list.ToArray();
            }

            protected override void OnUpdate(comm.TimeStat timeStat)
            {
                foreach (var space in _spaceDic.Values)
                {
                    space.Update(timeStat);
                }
            }
        }
        #endregion

        public int AllAreaCount { get { return _areaDic.Count; } }
        public int AllFactionCount { get { return _facDic.Count; } }
        public World World { get; private set; }
        public string StarId { get; private set; }

        Dictionary<string, Area> _areaDic = new Dictionary<string, Area>();
        Dictionary<string, Faction> _facDic = new Dictionary<string, Faction>();
        comm.objst.DStar _dStar = null;
        comm.objst.DStar.Data _dStarData = null;
        comm.objst.TableStar _tableStar = null;

        public Star(World world, string starId)
        {
            World = world;
            StarId = starId;

            _tableStar = World.DataManager.TableStar.GetStar(StarId);

            _dStar = World.DefineFile.GetStar(starId);
            _dStarData = World.DefineFile.GetStarData(StarId);

            foreach (var areaKey in _dStarData.AreaDic.Keys)
            {
                _areaDic.Add(areaKey, new Area(this, _dStarData.AreaDic[areaKey]));
            }

            foreach (var facKey in _dStarData.FactionDic.Keys)
            {
                var facId = ToRealFactionId(facKey);
                _facDic.Add(facId, new Faction(this, facId, _dStarData.FactionDic[facKey]));
            }
        }

        public void StatNewHero(string playerId, string factionId)
        {
            World.DataManager.TableStar.UpdateStat(StarId, heroCount: 1);
            World.DataManager.TableFaction.UpdateStat(factionId, heroCount: 1);
        }

        public string ToRealFactionId(string factionId)
        {
            return _dStar.ToRealFactionId(factionId);
        }
        public string ToRealSpaceId(string spaceId)
        {
            return _dStar.ToRealSpaceId(spaceId);
        }

        public void Save()
        {
            World.DataManager.TableStar.UpdateStar(_tableStar);
            var allArea = GetAllArea();
            foreach (var area in allArea)
            {
                area.Save();
            }
            var allFaction = GetAllFaction();
            foreach (var fac in allFaction)
            {
                fac.Save();
            }
        }
        public Faction GetFaction(string facrionId)
        {
            Faction obj = null;
            _facDic.TryGetValue(facrionId, out obj);
            return obj;
        }
        public Faction[] GetAllFaction()
        {
            var list = new List<Faction>();
            foreach (var item in _facDic.Values)
            {
                list.Add(item);
            }
            return list.ToArray();
        }
        public bool FindBaseSpace(string spaceId)
        {
            var facs = GetAllFaction();
            foreach (var fac in facs)
            {
                if (fac.FindBaseSpace(spaceId))
                {
                    return true;
                }
            }
            return false;
        }

        public Area GetArea(string areaId)
        {
            Area obj = null;
            _areaDic.TryGetValue(areaId, out obj);
            return obj;
        }
        public Area[] GetAllArea()
        {
            var list = new List<Area>();
            foreach (var item in _areaDic.Values)
            {
                list.Add(item);
            }
            return list.ToArray();
        }

        public SSpace GetSpace(string spaceId)
        {
            foreach (var area in _areaDic.Values)
            {
                var space = area.GetSpace(spaceId);
                if (space != null)
                {
                    return space;
                }
            }
            return null;
        }
        public comm.objst.SpaceInfo[] GetAllSpaceInfo(string areaId = comm.objst.DStar.Data.DEFAULT_AREA_KEY)
        {
            var area = GetArea(areaId);
            if (area != null)
            {
                return area.GetAllSpaceInfo();
            }
            return null;
        }
        public comm.objst.StarInfo GetStarInfo()
        {
            return new comm.objst.StarInfo
            {
                StarId = StarId,
                CanCreateHeroByPlayer = _dStar.CanCreateHeroByPlayer
            };
        }
        public comm.objst.FactionInfo[] GetAllFactionInfo()
        {
            var list = new List<comm.objst.FactionInfo>();
            foreach (var fac in _facDic.Values)
            {
                list.Add(fac.GetFactionInfo());
            }
            return list.ToArray();
        }

        protected override void OnUpdate(comm.TimeStat timeStat)
        {
            foreach (var area in _areaDic.Values)
            {
                area.Update(timeStat);
            }
        }
    }
    //#endregion
}
